package com.yangrd.helper.crudhelper.core.crud.page;

import org.springframework.context.ApplicationContext;
import org.springframework.data.jpa.repository.JpaRepository;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * description: DomainMappingManager
 *
 * @author: yangrd
 * @date: 2020/9/14
 * @version: 1.0
 */
public class DomainMappingManager {

    private Map<String, JpaRepository<Object, Object>> domain4Repositories = new HashMap<>();
    private Map<String, Class<?>> domain4Class = new HashMap<>();
    private Map<String, Class<?>> domain4IdClass = new HashMap<>();

    public DomainMappingManager(ApplicationContext applicationContext) {
        Map<String, JpaRepository> repositoryBeans = applicationContext.getBeansOfType(JpaRepository.class);
        repositoryBeans.forEach((repositoryName, repositoryBean) -> {
            Class<?> domainClass = MappingSupport.getDomainClass(repositoryBean);
            Class<?> domainIdClass = MappingSupport.getDomainIdClass(repositoryBean);
            String domainName = MappingSupport.getDomainName(domainClass);
            domain4Repositories.put(domainName, repositoryBean);
            domain4Class.put(domainName, domainClass);
            domain4IdClass.put(domainName, domainIdClass);
        });
    }

    public Optional<JpaRepository<Object, Object>> getJpaRepository(String domainName) {
        return Optional.ofNullable(domain4Repositories.get(domainName));
    }

    public Collection<Class<?>> listDomainClass(){
        return domain4Class.values();
    }

    public Class<?> getDomainClass(String domainName) {
        return domain4Class.get(domainName);
    }

    public Class<?> getIdClass(String domainName) {
        return domain4IdClass.get(domainName);
    }

    public static class MappingSupport {
        static Class<?> getDomainClass(JpaRepository<?, ?> jpaRepository) {
            Type[] jpaInterfacesTypes = jpaRepository.getClass().getGenericInterfaces();
            Type[] type = ((ParameterizedType) ((Class<?>) jpaInterfacesTypes[0]).getGenericInterfaces()[0]).getActualTypeArguments();
            return (Class<?>) type[0];
        }

        static Class<?> getDomainIdClass(JpaRepository<?, ?> jpaRepository) {
            Type[] jpaInterfacesTypes = jpaRepository.getClass().getGenericInterfaces();
            Type[] type = ((ParameterizedType) ((Class<?>) jpaInterfacesTypes[0]).getGenericInterfaces()[0]).getActualTypeArguments();
            return (Class<?>) type[1];
        }

        public static String getDomainName(Class<?> clazz) {
            String simpleName = clazz.getSimpleName();
            return simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
        }
    }
}
